﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using TagLib;

using LastFmClient;
using LyricWikiWebService;

namespace TranscoderSharp
{
    public class AudioFile
    {
        private static object LockObject = new object();

        public AudioFile(string FilePath)
        {
            m_FilePath = FilePath;
            m_FileInfo = new System.IO.FileInfo(m_FilePath);
            this.Status = EncodingStatus.Unencoded;
        }

        ~AudioFile()
        {
            DeleteTempFile();
        }

        private string m_FilePath;
        public string FilePath
        {
            get { return m_FilePath; }
            set { m_FilePath = value; }
        }

        private File m_TagLibFile;
        internal File TagLibFile
        {
            get
            {
                try
                {
                    if (m_TagLibFile == null)
                        m_TagLibFile = File.Create(m_FilePath);
                }
                catch (Exception e)
                {
                    m_TagLibFile = null;
                }

                return m_TagLibFile;
            }
            set { m_TagLibFile = value; }
        }

        /// <summary>
        /// returns filename without extension
        /// </summary>
        private string FileName
        {
            get
            {
                string str = System.IO.Path.GetFileNameWithoutExtension(this.m_FilePath);
                if (str.Length > 260)
                    str = str.Substring(0, 259);

                return str;
            }
        }

        internal string Extension
        {
            get
            {
                return System.IO.Path.GetExtension(this.FilePath);
            }
        }


        // public properties

        /// <summary>
        /// Title of song, if we cant read the tag or no title is set, we return the filename.
        /// </summary>
        public string Title
        {
            get
            {
                if (TagLibFile == null || TagLibFile.Tag.Title == null || TagLibFile.Tag.Title == string.Empty)
                    return FileName;

                return TagLibFile.Tag.Title;
            }
        }

        public string Artist
        {
            get
            {
                if (TagLibFile == null)
                    return string.Empty;

                return TagLibFile.Tag.JoinedPerformers;
            }
        }

        public string Album
        {
            get
            {
                return TagLibFile == null ? String.Empty : TagLibFile.Tag.Album;
            }
        }

        public uint Tracknumber
        {
            get
            {
                return TagLibFile == null ? 0 : TagLibFile.Tag.Track;
            }
        }

        private Artist m_LastFmArtist;
        private Artist LastFmArtist
        {
            get
            {
                if (m_LastFmArtist == null)
                    m_LastFmArtist = new Artist(this.Artist);

                return m_LastFmArtist;
            }
        }

        private Track m_LastFmTrack;
        private Track LastFmTrack
        {
            get
            {
                if (m_LastFmTrack == null)
                    m_LastFmTrack = new Track(this.Title, this.Artist);

                return m_LastFmTrack;
            }
        }

        public string TopGenre
        {
            get
            {
                try
                {
                    return this.Genres.First();
                    //return m_TagLibFile.Tag.Genres.First();
                }
                catch (Exception e)
                {
                    return string.Empty;
                }
            }
        }

        /*
        public string JoinedGenres
        {
            get
            {
                try
                {
                    return string.Join(",", this.LastFmArtist.Top5Tags.Select(p => p.Name).ToArray());
                }
                catch (Exception e)
                {
                    return string.Empty;
                }
            }
        }
         */

        public string[] Genres
        {
            get
            {
                try
                {
                    if (m_TagLibFile.Tag.Genres == null
                    || m_TagLibFile.Tag.Genres.Length == 0
                    || m_TagLibFile.Tag.Genres[0].ToLower() == "unknown"
                    || m_TagLibFile.Tag.Genres[0].ToLower() == "other")
                        return this.LastFmArtist.Top5Tags.Select(p => p.Name).ToArray();
                    else if (m_TagLibFile.Tag.Genres != null)
                        return m_TagLibFile.Tag.Genres;
                }
                catch (Exception e)
                {

                }

                return null;
            }
        }

        public void CopyTagToFile(string FilePath)
        {
            File FileToReceiveTag = File.Create(FilePath);
            FileToReceiveTag.Tag.Album = m_TagLibFile.Tag.Album;
            FileToReceiveTag.Tag.AlbumArtists = m_TagLibFile.Tag.AlbumArtists;
            //FileToReceiveTag.Tag.Artists = m_TagLibFile.Tag.Artists;
            FileToReceiveTag.Tag.Performers = m_TagLibFile.Tag.Performers;
            FileToReceiveTag.Tag.Title = m_TagLibFile.Tag.Title;
            FileToReceiveTag.Tag.Track = m_TagLibFile.Tag.Track;
            
            if (this.Genres != null && this.Genres.Any())
                FileToReceiveTag.Tag.Genres = new string[1] { this.Genres.First() };

            //if (m_TagLibFile.Tag.Genres == null
            //    || m_TagLibFile.Tag.Genres.Length == 0
            //    || m_TagLibFile.Tag.Genres[0].ToLower() == "unknown"
            //    || m_TagLibFile.Tag.Genres[0].ToLower() == "other")
            //    //FileToReceiveTag.Tag.Genres = new Artist(this.Artist).Top5Tags.Select(p => p.Name).ToArray();
            //    FileToReceiveTag.Tag.Genres = this.LastFmArtist.Top5Tags.Select(p => p.Name).ToArray();
            //else
            //    FileToReceiveTag.Tag.Genres = m_TagLibFile.Tag.Genres;

            FileToReceiveTag.Tag.TrackCount = m_TagLibFile.Tag.TrackCount;
            FileToReceiveTag.Tag.Year = m_TagLibFile.Tag.Year;
            FileToReceiveTag.Tag.Lyrics = GetLyrics();
            
            FileToReceiveTag.Save();
        }

        private string GetLyrics()
        {
            try
            {
                if (string.IsNullOrEmpty(Artist) || string.IsNullOrEmpty(Title))
                    return string.Empty;

                string lyrics = new LyricWiki().getSong(Artist, Title).lyrics;

                if (lyrics == "Not found")
                    return string.Empty;

                return lyrics;
            }
            catch
            {
                return string.Empty;
            }
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            // title is only thing we can be certain of having...
            sb.Append(Title);

            if (Artist != null && Artist != string.Empty)
                sb.Append(" by " + Artist);

            if (Album != null && Album != string.Empty)
                sb.Append(" from " + Album);

            return sb.ToString();

            //return Title + " by " + Artist + " from " + Album;
        }


        //internal bool IsValid
        //{
        //    get
        //    {
        //        return Transcoder.ValidateMp3(this);
        //    }
        //}

        public enum EncodingStatus { Unencoded, Decoded, Encoded, Skipped, Error };
        private EncodingStatus m_EncodingStatus = EncodingStatus.Unencoded;
        public EncodingStatus Status
        {
            get
            {
                return m_EncodingStatus;
            }
            set
            {
                m_EncodingStatus = value;
            }
        }

        internal TimeSpan EncodingTime { get; set; }

        internal string Mp3ValidationResult { get; set; }

        public string GetMp3ValidationResult()
        {
            return Mp3ValidationResult;
        }

        private Guid m_Guid;
        internal Guid Guid
        {
            get
            {
                if (m_Guid == Guid.Empty)
                    m_Guid = Guid.NewGuid();
                return m_Guid;
            }
        }

        private string m_TempFilePath;
        internal string TempFilePath
        {
            get
            {
                if (string.IsNullOrEmpty(m_TempFilePath))
                    m_TempFilePath = Transcoder.GetTempFileName(this.Extension);

                return m_TempFilePath;
            }
        }

        public void CopyToTempFile()
        {
            System.IO.File.Copy(this.FilePath, TempFilePath, true);
        }

        public void DeleteTempFile()
        {
            lock (LockObject)
            {
                if (System.IO.File.Exists(this.TempFilePath))
                    System.IO.File.Delete(this.TempFilePath);
                if (System.IO.File.Exists(this.TempFilePath + ".bak"))
                    System.IO.File.Delete(this.TempFilePath + ".bak");
            }
        }

//        public ConfigObject ConfigObject { get; set; }


        private System.IO.FileInfo m_FileInfo;
        internal System.IO.FileInfo FileInfo
        {
            get
            {
                return m_FileInfo;
            }
        }

        /// <summary>
        /// Size in bytes
        /// </summary>
        internal long Size
        {
            get
            {
                try
                {

                    return m_FileInfo.Length;
                }
                catch (Exception ex)
                {
                    return 0;
                }
            }
        }

        /// <summary>
        /// Size in megabytes
        /// </summary>
        public double SizeMB
        {
            get
            {
                return (this.Size / 1024f) / 1024f;  
                //return (this.Size / 1024f) / 1024f;
            }
        }

        public string SizeMBstring
        {
            get
            {
                return String.Format("{0:0.00}", this.SizeMB);  
                //return (this.Size / 1024f) / 1024f;
            }
        }


        internal int Timeout
        {
            get
            {
                //return 1;
                return Convert.ToInt32((this.SizeMB + 1) * 1.1);
            }
        }
        public string GetOutputFilePath(string OutputDirectory, string extension, string OutputFormatPath)
        {
            if (this == null)
                throw new Exception("Can not get output path because Audio file is null");

            if (String.IsNullOrEmpty(extension))
                throw new Exception("Can not get output path because the extension is null or empty");

            // should validate the output directory here
            if (!System.IO.Path.IsPathRooted(OutputDirectory))
                throw new Exception("Output Directory is invalid, must be an absolute path. " + OutputDirectory);

            if (OutputDirectory.Length > 260)
                throw new Exception("Output directory is invalid, must be no more than 260 characters. " + OutputDirectory);

            StringBuilder sb = new StringBuilder(OutputFormatPath);
            sb.Capacity = 255;

            string title = string.IsNullOrEmpty(this.Title) ? "Unknown Title" : Transcoder.CleanString(this.Title);
            sb.Replace("{TITLE}", title);

            string album = string.IsNullOrEmpty(this.Album) ? "Unknown Album" : Transcoder.CleanString(this.Album);
            sb.Replace("{ALBUM}", album);

            string artist = string.IsNullOrEmpty(this.Artist) ? "Unknown Artist" : Transcoder.CleanString(this.Artist);
            sb.Replace("{ARTIST}", artist);

            string tracknumber = this.Tracknumber == 0 ? "0" : this.Tracknumber.ToString();
            sb.Replace("{TRACKNUMBER}", tracknumber);

            if (sb.Length > 250)
            {
                sb.Remove(0, sb.Length);
                sb.Append(this.FileInfo.Name);
            }

            sb.Append(extension);

            string FullPath = System.IO.Path.Combine(OutputDirectory, sb.ToString());


            // here we split in to directory and file name so we can remove . chars from the directory path
            string DirectoryPath = System.IO.Path.GetDirectoryName(FullPath);
            string FileName = System.IO.Path.GetFileName(FullPath);

            //DirectoryPath = DirectoryPath.Replace(".", "");

            if (!System.IO.Directory.Exists(DirectoryPath))
                System.IO.Directory.CreateDirectory(DirectoryPath);

            string output = System.IO.Path.Combine(DirectoryPath, FileName);

            byte[] asciiChars = Encoding.ASCII.GetBytes(output);
            string result = Encoding.ASCII.GetString(asciiChars);
            System.Diagnostics.Debug.WriteLine(result);

            return result;
        }
    }   
}
